#import "ufoView.h"
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#import <QuartzCore/QuartzCore.h>

#include "render.h"
#include "system.h"
#include "log.h"

int g_useTimer = 0;
int g_useOpengl = 1;

int internal_route_sizeChanged(void* wParam, void* lParam);
int internal_route_touchMove(void* wParam, void* lParam);
int internal_route_touchDown(void* wParam, void* lParam);
int internal_route_touchUp(void* wParam, void* lParam);
int internal_route_touchWheel(void* wParam, void* lParam);
int internal_route_ncHitTest(void* wParam, void* lParam);

@implementation ufoView

- (NSOpenGLContext*) openGLContext
{
	return openGLContext;
}

- (NSOpenGLPixelFormat*) pixelFormat
{
	return pixelFormat;
}

- (id) initWithFrame:(NSRect)frameRect
{
    NSOpenGLPixelFormatAttribute attribs[] =
    {
        NSOpenGLPFAAccelerated,
        //NSOpenGLPFABackingStore,
        NSOpenGLPFADoubleBuffer,
        
        NSOpenGLPFAAlphaSize, 8,
		NSOpenGLPFAColorSize, 24,
        NSOpenGLPFADepthSize, 24,
        NSOpenGLPFAStencilSize, 8,
        
        /*NSOpenGLPFAMultisample,
        NSOpenGLPFASampleBuffers, 2,
        NSOpenGLPFASamples, 4,*/
        0
    };
	
    pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
	openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
    
	if (self = [super initWithFrame:frameRect]) {
        if (g_useOpengl) {
            [[self openGLContext] makeCurrentContext];
        }
    }
    
    NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0, 2000, 2000) options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow) owner:self userInfo:nil];
    [self addTrackingArea:trackingArea];
    
    [self setAcceptsTouchEvents:YES];
    [self setWantsRestingTouches:YES];
    
    if (g_useTimer) {
        NSTimer *renderTimer = [NSTimer timerWithTimeInterval:1.0f/150.0f target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSEventTrackingRunLoopMode];
        
        if (ufoGetAppInfo()->winStyle == LAYERED_WIN) {
            GLint opq = 0;
            [[self openGLContext] setValues:&opq forParameter:NSOpenGLCPSurfaceOpacity];
        }
    }
    
    [[NSNotificationCenter defaultCenter] addObserver:self
     selector:@selector(reshape)
     name:NSViewGlobalFrameDidChangeNotification
     object:self];
    
    return self;
}

- (void)prepareOpenGL
{
    if (g_useOpengl) {
    
    if (!g_useTimer) {
        GLint swapInt = 1;
        [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
        
        if (ufoGetAppInfo()->winStyle == LAYERED_WIN) {
            GLint opq = 0;
            [[self openGLContext] setValues:&opq forParameter:NSOpenGLCPSurfaceOpacity];
        }

        CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
    
        CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
    
        CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
        CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
        CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
    
        CVDisplayLinkStart(displayLink);
    }
    }
}

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
    CVReturn result = [(ufoView*)displayLinkContext getFrameForTime:outputTime];
    return result;
}

- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
{
    ufoRuningLoop([self openGLContext]);
    return kCVReturnSuccess;
}

- (void)timerFired:(id)sender
{
    ufoRuningLoop([self openGLContext]);
}

- (void)drawRect:(NSRect)rect
{
    ufoRuningLoop([self openGLContext]);
}

- (BOOL)isOpaque
{
    if (ufoGetAppInfo()->winStyle == LAYERED_WIN)
        return NO;
    return YES;
}

/*- (NSView *)hitTest:(NSPoint)aPoint
{
    NSView *last = [super hitTest:aPoint];
    return nil;
}*/

-(BOOL)canBecomeKeyView
{
    return YES;
}

-(BOOL)isFlipped
{
    return YES;
}

- (void) lockFocus
{
	[super lockFocus];
    
    if (g_useOpengl) {
        if ([[self openGLContext] view] != self)
            [[self openGLContext] setView:self];
    }
}

- (void)reshape
{
    if (g_useOpengl) {
    
        [[self openGLContext] update];
        NSRect rect = [self frame];
    
        [[self openGLContext] makeCurrentContext];
   
        CGLLockContext([[self openGLContext] CGLContextObj]);
    
        ufoRect rc = {0, 0, rect.size.width, rect.size.height};
        internal_route_sizeChanged(&rc, 0);
        
        [[self openGLContext] update];
        CGLUnlockContext([[self openGLContext] CGLContextObj]);
    }
}

- (void)mouseUp:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {pt.x, pt.y};
    internal_route_touchUp(&uPt, 0);
    drag = 0;
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)mouseDown:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {pt.x, pt.y};
    
    NSRect  windowFrame = [[self window] frame];
    
    initialLocation = [NSEvent mouseLocation];
    initialLocation.x -= windowFrame.origin.x;
    initialLocation.y -= windowFrame.origin.y;
    
    drag = internal_route_ncHitTest(&uPt, 0);
    
    if (g_useOpengl) {
        ufoPoint uPt2 = {pt.x, pt.y};
        internal_route_touchDown(&uPt2, 0);
    }
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)mouseMoved:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {pt.x, pt.y};
    
    if (g_useOpengl) {
        internal_route_touchMove(&uPt, 0);
    }
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)mouseEntered:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {0};
    
    if (pt.x >= 0)
        uPt.x = pt.x + 0.99999;
    else
        uPt.x = pt.x - 0.99999;
    
    if (pt.y >= 0)
        uPt.y = pt.y + 0.99999;
    else
        uPt.y = pt.y - 0.99999;
    
    if (g_useOpengl) {
        internal_route_touchMove(&uPt, 0);
    }
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)mouseExited:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {0};
    
    if (pt.x >= 0)
        uPt.x = pt.x + 0.99999;
    else
        uPt.x = pt.x - 0.99999;
    
    if (pt.y >= 0)
        uPt.y = pt.y + 0.99999;
    else
        uPt.y = pt.y - 0.99999;
    
    if (g_useOpengl) {
        internal_route_touchMove(&uPt, 0);
    }
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)mouseDragged:(NSEvent *)theEvent
{
    CGLLockContext([[self openGLContext] CGLContextObj]);
    
    if (drag) {
        NSPoint currentLocation;
        NSPoint newOrigin;
        NSRect  screenFrame = [[NSScreen mainScreen] frame];
        NSRect  windowFrame = [self frame];
        currentLocation = [NSEvent mouseLocation];
        newOrigin.x = currentLocation.x - initialLocation.x;
        newOrigin.y = currentLocation.y - initialLocation.y;
        // Don't let window get dragged up under the menu bar
        if( (newOrigin.y+windowFrame.size.height) > (screenFrame.origin.y+screenFrame.size.height) ){
            newOrigin.y=screenFrame.origin.y + (screenFrame.size.height-windowFrame.size.height);
        }
        //go ahead and move the window to the new location
        [[self window] setFrameOrigin:newOrigin];
    }
    else {
        NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
        ufoPoint uPt = {pt.x, pt.y};
        internal_route_touchMove(&uPt, 0);
    }
    
    CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

- (void)scrollWheel:(NSEvent *)theEvent
{
    NSPoint pt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    ufoPoint uPt = {pt.x, pt.y};
    float a = [theEvent deltaY];
    int b = a*10.0f;
    //internal_route_touchWheel(&uPt, (void*)b);
}

- (void)keyDown:(NSEvent *)theEvent
{
    
}

- (void)keyUp:(NSEvent *)theEvent
{
    
}

@end